// 基于管道的进程池设计
#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cassert>
#include <vector>
#include <string>
#include <ctime>
#include<sys/wait.h>
#include<sys/types.h>
// using namespace std;

#define PROCESS_NUM 5

#define MakeSeed() srand((unsigned long)time(nullptr) ^ getpid() ^ 0x171237 ^ rand() % 1234)

///////////////////////////////////子进程要完成的某种任务
// 函数指针
typedef void (*func_t)();

void downloadtask()
{
    sleep(1);
    std::cout << getpid() << ": 下载任务\n"
              << std::endl;
}

void iotask()
{
    sleep(1);
    std::cout << getpid() << ": IO任务\n"
              << std::endl;
}

void flushtask()
{
    sleep(1);
    std::cout << getpid() << ": 刷新任务\n"
              << std::endl;
}

void loadtaskfunc(std::vector<func_t> *out)
{
    assert(out);
    out->push_back(downloadtask);
    out->push_back(iotask);
    out->push_back(flushtask);
}

////////////////////////////////////下面的代码是一个多进程程序
class subep
{
public:
    subep(pid_t subid, int writefd)
        : _subid(subid), _writefd(writefd)
    {
        char namebuffer[1024];
        snprintf(namebuffer, sizeof namebuffer, "process-%d[pid(%d)-fd(%d)]", num++, _subid, _writefd);
        _name = namebuffer;
    }

public:
    static int num;
    std::string _name;
    pid_t _subid;
    int _writefd;
};
int subep::num = 0;

int recvtask(int readfd)
{
    int code = 0;
    ssize_t s = read(readfd, &code, sizeof code);
    //assert(s == sizeof(int));
    if(s==4)
    {
        return code;
    }
    else if(s == 0)
    {
        return -1;
    }
    else
    {
        return -1;
    }
}

void createsubprocess(std::vector<subep> *subs, std::vector<func_t> &funcmap)
{
    std::vector<int> deletefd;
    for (int i = 0; i < PROCESS_NUM; i++)
    {
        int fds[2];
        int n = pipe(fds);
        assert(n == 0);
        (void)n;

        pid_t id = fork();
        if (id == 0)
        {
            for(int i = 0;i < deletefd.size();i++)
            {
                close(deletefd[i]);
            }

            // 子进程，进行处理任务0
            close(fds[1]);
            // 1、获取命令码，如果没有发送，子进程应该阻塞
            while (true)
            {
                int commandcode = recvtask(fds[0]);
                if (commandcode >= 0 && commandcode < funcmap.size())
                {
                    funcmap[commandcode]();
                }
                else if(commandcode == -1)
                {
                    break;
                }
            }
            // 2、完成任务
            //std::cout << "sub recv code error!" << std::endl;
            exit(0);
        }

        close(fds[0]);

        subep sub(id, fds[1]);
        subs->push_back(sub);
        deletefd.push_back(fds[1]);
    }
}

void sendtesk(const subep &process, int tasknum)
{
    std::cout << "send task num: " << tasknum << " send to " << process._name << std::endl;
    int n = write(process._writefd, &tasknum, sizeof(tasknum));
    assert(n == sizeof(int));
    (void)n;
}

void loadblancecontrl(const std::vector<subep> &subs, const std::vector<func_t> &funcmap, int count)
{
    int processnum = subs.size();

    int tasknum = funcmap.size();

    bool forever = (count ==0 ? true : false);
    
    while (true)
    {
        // 1、先选择一个子进程
        int subIdx = rand() % processnum;

        // 2、选择一个任务
        int taskIdx = rand() % tasknum;

        // 3、任务发送给选择的进程
        sendtesk(subs[subIdx], taskIdx);

        sleep(1);

        if(forever)
        {

        }
        else
        {
            count--;
            if(count == 0)
            {
                break;
            }
        }
    }
    for(int i = 0;i < processnum;i++)
    {
        close(subs[i]._writefd);
    }
}

void waitprocess(std::vector<subep>& processes)
{
    int processnum = processes.size();
    for(int i = 0;i < processnum;i++)
    {
        waitpid(processes[i]._subid,nullptr,0);
        std::cout<<"wait sub process success ... "<< processes[i]._subid<<std::endl;
    }
}

int main()
{
    MakeSeed();
    // 1、建立子进程并建立和子进程通信的信道
    std::vector<func_t> funcmap;

    loadtaskfunc(&funcmap);

    std::vector<subep> subs;

    createsubprocess(&subs, funcmap);

    // 2、走到这里就是父进程，可以控制子进程

    int taskcnt = 3;
    std::cin>>taskcnt;
    loadblancecontrl(subs, funcmap, taskcnt);

    // 3、回收子进程信息
    waitprocess(subs);

    return 0;
}